package com.wechat.gateway.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

/**
 * RSA非对称加密解密工具类
 */
@Slf4j
@Component
public class CryptoUtil {

    @Value("${crypto.rsa.public-key:}")
    private String publicKeyStr;

    @Value("${crypto.rsa.private-key:}")
    private String privateKeyStr;

    @Value("${crypto.rsa.key-size:2048}")
    private int keySize;

    /**
     * -- GETTER --
     *  获取当前加密模式
     */
    @Getter
    @Value("${crypto.mode:RSA}")
    private String cryptoMode; // RSA 或 AES

    @Value("${crypto.aes.secret-key:wechat-gateway-crypto-secret-key-2025}")
    private String aesSecretKey;

    private RSA rsa;
    private AES aes;

    /**
     * 生成新的RSA密钥对
     */
    public static void generateRSAKeyPair(int keySize) {
        RSA rsa = SecureUtil.rsa();
        System.out.println("=== 新生成的RSA密钥对 ===");
        System.out.println("Public Key: " + rsa.getPublicKeyBase64());
        System.out.println("Private Key: " + rsa.getPrivateKeyBase64());
        System.out.println("========================");
    }

    /**
     * 主方法：用于生成密钥对
     */
    public static void main(String[] args) {
        generateRSAKeyPair(2048);
    }

    @PostConstruct
    public void init() {
        if ("RSA".equalsIgnoreCase(cryptoMode)) {
            initRSA();
        } else {
            initAES();
        }
    }

    /**
     * 初始化RSA加密器
     */
    private void initRSA() {
        try {
            if (StrUtil.isNotEmpty(publicKeyStr) && StrUtil.isNotEmpty(privateKeyStr)) {
                // 使用配置的密钥对
                log.info("使用配置的RSA密钥对");
                rsa = SecureUtil.rsa(privateKeyStr, publicKeyStr);
            } else {
                // 生成新的密钥对
                log.info("生成新的RSA密钥对，密钥长度: {}", keySize);
                rsa = SecureUtil.rsa();

                // 输出密钥对供配置使用
                String publicKey = rsa.getPublicKeyBase64();
                String privateKey = rsa.getPrivateKeyBase64();

                log.warn("=== RSA密钥对已生成，请保存到配置文件 ===");
                log.warn("Public Key: {}", publicKey);
                log.warn("Private Key: {}", privateKey);
                log.warn("==========================================");
            }

            // 验证RSA加密功能
            if (validateRSACrypto()) {
                log.info("RSA加密解密初始化成功");
            } else {
                log.error("RSA加密解密验证失败");
                throw new RuntimeException("RSA加密解密初始化失败");
            }

        } catch (Exception e) {
            log.error("RSA初始化失败: {}", e.getMessage(), e);
            throw new RuntimeException("RSA初始化失败", e);
        }
    }

    /**
     * 初始化AES加密器（备用方案）
     */
    private void initAES() {
        try {
            String key = SecureUtil.md5(aesSecretKey);
            aes = SecureUtil.aes(key.getBytes(StandardCharsets.UTF_8));

            if (validateAESCrypto()) {
                log.info("AES加密解密初始化成功");
            } else {
                log.error("AES加密解密验证失败");
                throw new RuntimeException("AES加密解密初始化失败");
            }
        } catch (Exception e) {
            log.error("AES初始化失败: {}", e.getMessage(), e);
            throw new RuntimeException("AES初始化失败", e);
        }
    }

    /**
     * 加密字符串
     * RSA模式：使用公钥加密
     * AES模式：使用对称密钥加密
     *
     * @param plainText 明文
     * @return 加密后的Base64字符串
     */
    public String encrypt(String plainText) {
        if (StrUtil.isEmpty(plainText)) {
            return plainText;
        }

        try {
            if ("RSA".equalsIgnoreCase(cryptoMode)) {
                return encryptByRSA(plainText);
            } else {
                return encryptByAES(plainText);
            }
        } catch (Exception e) {
            log.error("加密失败: {}", e.getMessage(), e);
            throw new RuntimeException("数据加密失败", e);
        }
    }

    /**
     * 解密字符串
     * RSA模式：使用私钥解密
     * AES模式：使用对称密钥解密
     *
     * @param encryptedText 加密后的Base64字符串
     * @return 解密后的明文
     */
    public String decrypt(String encryptedText) {
        if (StrUtil.isEmpty(encryptedText)) {
            return encryptedText;
        }

        try {
            if ("RSA".equalsIgnoreCase(cryptoMode)) {
                return decryptByRSA(encryptedText);
            } else {
                return decryptByAES(encryptedText);
            }
        } catch (Exception e) {
            log.error("解密失败: {}", e.getMessage(), e);
            throw new RuntimeException("数据解密失败", e);
        }
    }

    /**
     * RSA加密
     * 对于长文本，分段加密
     */
    private String encryptByRSA(String plainText) {
        if (rsa == null) {
            throw new RuntimeException("RSA未初始化");
        }

        // RSA每次加密的数据长度有限制，2048位密钥最多245字节
        int maxEncryptBlock = keySize / 8 - 11; // PKCS1Padding需要减去11字节
        byte[] data = plainText.getBytes(StandardCharsets.UTF_8);

        if (data.length <= maxEncryptBlock) {
            // 数据较小，直接加密
            return rsa.encryptBase64(plainText, KeyType.PublicKey);
        } else {
            // 数据较大，分段加密
            StringBuilder result = new StringBuilder();
            int offset = 0;
            while (offset < data.length) {
                int length = Math.min(maxEncryptBlock, data.length - offset);
                byte[] segment = new byte[length];
                System.arraycopy(data, offset, segment, 0, length);

                byte[] encryptedSegment = rsa.encrypt(segment, KeyType.PublicKey);
                result.append(Base64.encode(encryptedSegment)).append(",");

                offset += length;
            }
            // 移除最后的逗号
            if (!result.isEmpty()) {
                result.setLength(result.length() - 1);
            }
            return result.toString();
        }
    }

    /**
     * RSA解密
     * 对于分段加密的数据，分段解密
     */
    private String decryptByRSA(String encryptedText) {
        if (rsa == null) {
            throw new RuntimeException("RSA未初始化");
        }

        if (!encryptedText.contains(",")) {
            // 单段数据，直接解密
            return rsa.decryptStr(encryptedText, KeyType.PrivateKey);
        } else {
            // 多段数据，分段解密
            String[] segments = encryptedText.split(",");
            StringBuilder result = new StringBuilder();

            for (String segment : segments) {
                byte[] encryptedBytes = Base64.decode(segment);
                byte[] decryptedBytes = rsa.decrypt(encryptedBytes, KeyType.PrivateKey);
                result.append(new String(decryptedBytes, StandardCharsets.UTF_8));
            }

            return result.toString();
        }
    }

    /**
     * AES加密
     */
    private String encryptByAES(String plainText) {
        if (aes == null) {
            throw new RuntimeException("AES未初始化");
        }
        return aes.encryptBase64(plainText);
    }

    /**
     * AES解密
     */
    private String decryptByAES(String encryptedText) {
        if (aes == null) {
            throw new RuntimeException("AES未初始化");
        }
        return aes.decryptStr(encryptedText);
    }

    /**
     * 加密字节数组
     */
    public byte[] encrypt(byte[] plainBytes) {
        if (plainBytes == null || plainBytes.length == 0) {
            return plainBytes;
        }

        try {
            if ("RSA".equalsIgnoreCase(cryptoMode)) {
                String plainText = new String(plainBytes, StandardCharsets.UTF_8);
                String encrypted = encryptByRSA(plainText);
                return encrypted.getBytes(StandardCharsets.UTF_8);
            } else {
                return aes.encrypt(plainBytes);
            }
        } catch (Exception e) {
            log.error("字节数组加密失败: {}", e.getMessage(), e);
            throw new RuntimeException("字节数组加密失败", e);
        }
    }

    /**
     * 解密字节数组
     */
    public byte[] decrypt(byte[] encryptedBytes) {
        if (encryptedBytes == null || encryptedBytes.length == 0) {
            return encryptedBytes;
        }

        try {
            if ("RSA".equalsIgnoreCase(cryptoMode)) {
                String encryptedText = new String(encryptedBytes, StandardCharsets.UTF_8);
                String decrypted = decryptByRSA(encryptedText);
                return decrypted.getBytes(StandardCharsets.UTF_8);
            } else {
                return aes.decrypt(encryptedBytes);
            }
        } catch (Exception e) {
            log.error("字节数组解密失败: {}", e.getMessage(), e);
            throw new RuntimeException("字节数组解密失败", e);
        }
    }

    /**
     * 验证RSA加密解密功能
     */
    private boolean validateRSACrypto() {
        try {
            String testText = "Hello, WeChat Gateway RSA Encryption!";
            String encrypted = encryptByRSA(testText);
            String decrypted = decryptByRSA(encrypted);
            boolean isValid = testText.equals(decrypted);

            log.info("RSA加密解密验证: {}, 原文: {}, 解密: {}",
                    isValid ? "成功" : "失败", testText, decrypted);

            return isValid;
        } catch (Exception e) {
            log.error("RSA加密解密验证失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 验证AES加密解密功能
     */
    private boolean validateAESCrypto() {
        try {
            String testText = "Hello, WeChat Gateway AES Encryption!";
            String encrypted = encryptByAES(testText);
            String decrypted = decryptByAES(encrypted);
            boolean isValid = testText.equals(decrypted);

            log.info("AES加密解密验证: {}, 原文: {}, 解密: {}",
                    isValid ? "成功" : "失败", testText, decrypted);

            return isValid;
        } catch (Exception e) {
            log.error("AES加密解密验证失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 获取RSA公钥（用于客户端加密）
     */
    public String getPublicKey() {
        if (rsa != null) {
            return rsa.getPublicKeyBase64();
        }
        return null;
    }
}